﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Nomo.CoreModule;
using Nomo.UnityCoreModule.Enumerators;
using Nomo.UnityCoreModule.NodeBehaviours;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace Nomo.UnityCoreModule.Nodes
{
    /// <summary>
    /// 界面。
    /// </summary>
    [DisallowMultipleComponent]
    [RequireComponent(typeof(CanvasGroup))]
    [RequireComponent(typeof(RectTransform))]
    public abstract class Node : UIBehaviour, IEnumerable<Node>, ICanvasElement
    {
        #region class member

        private static          NodeRoot _nodeRoot;
        private static          float    _longTimeInactiveThreshold = float.PositiveInfinity;
        private static readonly Type     TypeOfNode                 = typeof(Node);

        /// <summary>
        /// 界面信息提供者。
        /// </summary>
        public static INodeInfoProvider NodeInfoProvider { get; set; }

        /// <summary>
        /// 获取或设置当界面处于非活跃状态经过多久之后，会被认为是“长时间处于非活跃状态”。
        /// </summary>
        public static float LongTimeInactiveThreshold { get => _longTimeInactiveThreshold; set => _longTimeInactiveThreshold = Mathf.Max(value, 0F); }

        /// <summary>
        /// 处于活跃状态且在显示序最靠前且的界面。
        /// </summary>
        public static Node UpperMostActive => ViewOrderAll().FirstOrDefault(e => e.IsActive());

        /// <summary>
        /// 清理不常驻并且长时间处于非活跃状态的界面。
        /// </summary>
        public static void ReleaseLongTimeUnusedNodes()
        {
            var now = DateTime.UtcNow;
            foreach (var node in PreOrderAll().Where(e => !e.eternal && e._state == NodeBehaviour.NodeState.Inactive && (now - e._lastActiveUtcDateTime).TotalSeconds >= _longTimeInactiveThreshold))
            {
                Destroy(node.gameObject);
            }
        }

        private static NodeRoot NodeRoot => _nodeRoot != null ? _nodeRoot : _nodeRoot = FindObjectOfType<NodeRoot>();

        private static void AlignToSafeArea(RectTransform rectTransform)
        {
            var safeArea = Screen.safeArea;
            var width    = Screen.width;
            var height   = Screen.height;
            rectTransform.anchorMin        = new Vector2(Mathf.InverseLerp(0F, width, safeArea.xMin), Mathf.InverseLerp(0F, height, safeArea.yMin));
            rectTransform.anchorMax        = new Vector2(Mathf.InverseLerp(0F, width, safeArea.xMax), Mathf.InverseLerp(0F, height, safeArea.yMax));
            rectTransform.anchoredPosition = Vector2.zero;
            rectTransform.sizeDelta        = Vector2.zero;
        }

        #region 枚举数

        /// <summary>
        /// 以前序返回所有根界面以及它们的所有子界面。
        /// </summary>
        /// <returns>可前序遍历所有根 <see cref="Node"/> 以及它们的所有子 <see cref="Node"/> 的枚举数。</returns>
        public static IEnumerable<Node> PreOrderAll()
        {
            foreach (var rootNode in NodeRoot.RootNodes)
            {
                foreach (var node in rootNode.PreOrder())
                {
                    yield return node;
                }
            }
        }

        /// <summary>
        /// 以层序返回所有根界面以及它们的所有子界面。
        /// </summary>
        /// <returns>可层序遍历所有根 <see cref="Node"/> 以及它们的所有子 <see cref="Node"/> 的枚举数。</returns>
        public static IEnumerable<Node> LevelOrderAll()
        {
            var queue = new Queue<Node>(NodeRoot.RootNodes);
            while (queue.Count != 0)
            {
                var node = queue.Dequeue();
                foreach (var child in node.NodeChildrenContainer.NodeChildren)
                {
                    queue.Enqueue(child);
                }
                yield return node;
            }
        }

        /// <summary>
        /// 以显示序返回所有根界面以及它们的所有子界面。
        /// </summary>
        /// <returns>可显示序遍历所有根 <see cref="Node"/> 以及它们的所有子 <see cref="Node"/> 的枚举数。</returns>
        public static IEnumerable<Node> ViewOrderAll()
        {
            foreach (var rootNode in NodeRoot.RootNodes.AsEnumerable().Reverse())
            {
                foreach (var node in rootNode.ViewOrder())
                {
                    yield return node;
                }
            }
        }

        #endregion

        #region 获取界面

        /// <summary>
        /// 获取指定类型的界面。
        /// </summary>
        /// <typeparam name="TNode">界面的类型。</typeparam>
        /// <returns>如果存在类型为 <typeparamref name="TNode"/> 的界面，则将其返回；否则返回 <see langword="null"/> .</returns>
        public static TNode Get<TNode>() where TNode : Node
        {
            return (TNode) GetNode(typeof(TNode));
        }

        /// <summary>
        /// 获取指定类型的界面。
        /// </summary>
        /// <param name="type">界面的类型。</param>
        /// <returns>如果存在类型为 <paramref name="type"/> 的界面，则将其返回；否则返回 <see langword="null"/> .</returns>
        public static Node Get(Type type)
        {
            if (type != null && TypeOfNode.IsAssignableFrom(type))
            {
                return GetNode(type);
            }
            return null;
        }

        private static Node GetNode(Type type)
        {
            return ViewOrderAll().FirstOrDefault(e => e._typeHierarchy.Contains(type));
        }

        #endregion

        #region 打开界面

        /// <summary>
        /// 打开指定类型的 <see cref="Node"/> .
        /// </summary>
        /// <typeparam name="TNode">界面的类型。</typeparam>
        /// <returns>打开的界面。</returns>
        public static TNode Open<TNode>() where TNode : Node
        {
            var nodeType = typeof(TNode);
            if (!nodeType.IsAbstract)
            {
                return (TNode) OpenRootNode(nodeType);
            }
            return null;
        }

        /// <summary>
        /// 打开指定类型的 <see cref="Node"/> ，并将其 <see cref="Parent"/> 设为指定类型的父 <see cref="Node"/> .
        /// </summary>
        /// <typeparam name="TNode">界面的类型。</typeparam>
        /// <typeparam name="TParentNode">界面的父界面的类型。</typeparam>
        /// <returns>打开的界面。</returns>
        public static TNode Open<TNode, TParentNode>() where TNode : Node where TParentNode : Node
        {
            var nodeType       = typeof(TNode);
            var parentNodeType = typeof(TParentNode);
            if (!nodeType.IsAbstract && !parentNodeType.IsAbstract)
            {
                return (TNode) OpenNonRootNode(nodeType, parentNodeType);
            }
            return null;
        }

        /// <summary>
        /// 打开指定类型的 <see cref="Node"/> .
        /// </summary>
        /// <param name="nodeType">界面的类型。</param>
        /// <returns>打开的界面。</returns>
        public static Node Open(Type nodeType)
        {
            if (nodeType != null && TypeOfNode.IsAssignableFrom(nodeType) && !nodeType.IsAbstract)
            {
                return OpenRootNode(nodeType);
            }
            return null;
        }

        /// <summary>
        /// 打开指定类型的 <see cref="Node"/> ，并将其 <see cref="Parent"/> 设为指定类型的父 <see cref="Node"/> .
        /// </summary>
        /// <param name="nodeType">界面的类型。</param>
        /// <param name="parentNodeType">界面的父界面的类型。</param>
        /// <returns>打开的界面。</returns>
        public static Node Open(Type nodeType, Type parentNodeType)
        {
            if (nodeType == null)
            {
                return null;
            }
            if (parentNodeType == null)
            {
                return null;
            }
            if (!TypeOfNode.IsAssignableFrom(nodeType))
            {
                return null;
            }
            if (!TypeOfNode.IsAssignableFrom(parentNodeType))
            {
                return null;
            }
            if (nodeType.IsAbstract)
            {
                return null;
            }
            if (parentNodeType.IsAbstract)
            {
                return null;
            }
            return OpenNonRootNode(nodeType, parentNodeType);
        }

        private static Node OpenRootNode(Type nodeType)
        {
            var node = GetNode(nodeType);
            if (node == null)
            {
                node = CreateNode(nodeType);
            }
            node.Parent = null;
            node.transform.SetAsLastSibling();
            node.PerformOpen();
            return node;
        }

        private static Node OpenNonRootNode(Type nodeType, Type parentNodeType)
        {
            var parentNode = GetNode(parentNodeType);
            if (parentNode == null)
            {
                return null;
            }
            var node = GetNode(nodeType);
            if (node == null)
            {
                node = CreateNode(nodeType);
            }
            node.Parent = parentNode;
            node.transform.SetAsLastSibling();
            node.PerformOpen();
            return node;
        }

        #endregion

        #region 创建界面

        private static Node CreateNode(Type nodeType)
        {
            if (NodeInfoProvider != null)
            {
                var nodeInfo = NodeInfoProvider.GetNodeInfo(nodeType);
                nodeInfo.Node.AssetBundle = nodeInfo.AssetBundle;
                return nodeInfo.Node;
            }
            return null;
        }

        #endregion

        #region 关闭界面

        /// <summary>
        /// 关闭指定类型的 <see cref="Node"/> .
        /// </summary>
        /// <typeparam name="TNode">界面的类型。</typeparam>
        public static void Close<TNode>() where TNode : Node
        {
            CloseNode(typeof(TNode));
        }

        /// <summary>
        /// 关闭指定类型的 <see cref="Node"/> .
        /// </summary>
        /// <param name="nodeType">界面的类型。</param>
        public static void Close(Type nodeType)
        {
            if (nodeType != null && TypeOfNode.IsAssignableFrom(nodeType))
            {
                CloseNode(nodeType);
            }
        }

        private static void CloseNode(Type nodeType)
        {
            var node = GetNode(nodeType);
            if (node != null)
            {
                node.PerformClose();
            }
        }

        #endregion

        #endregion

        #region instance member

        [SerializeField] private bool                  eternal;
        [SerializeField] private RectTransform[]       rectTransformsForAdaptation;
        [SerializeField] private GameObject[]          closeTriggers;
        [SerializeField] private NodeChildrenContainer childrenContainer;

        private readonly Type[]                  _typeHierarchy;
        private          NodeBehaviour.NodeState _state;
        private          Node                    _parent;
        private          CanvasGroup             _canvasGroup;
        private          RectTransform           _rectTransform;
        private          DateTime                _lastActiveUtcDateTime;
        private          Guid                    _performOpenGuid;
        private          Guid                    _performMainGuid;
        private          Guid                    _performCloseGuid;

        /// <inheritdoc />
        protected Node()
        {
            var types = new List<Type>();
            var type  = GetType();
            while (type != TypeOfNode && type != null)
            {
                types.Add(type);
                type = type.BaseType;
            }
            types.Add(TypeOfNode);
            _typeHierarchy = types.ToArray();
        }

        /// <summary>
        /// <see cref="Node"/> 的画布组。
        /// </summary>
        public CanvasGroup CanvasGroup => _canvasGroup != null ? _canvasGroup : _canvasGroup = GetComponent<CanvasGroup>();

        /// <summary>
        /// <see cref="Node"/> 的矩形变换。
        /// </summary>
        public RectTransform RectTransform => _rectTransform != null ? _rectTransform : _rectTransform = (RectTransform) transform;

        /// <summary>
        /// <see cref="Node"/> 的资源包。
        /// </summary>
        public AssetBundle AssetBundle { get; private set; }

        /// <summary>
        /// 一个可作为 <see cref="CoroutineRunner.Stop"/> 的参数，用以停止 <see cref="PerformMain"/> 的全局唯一标识符。
        /// </summary>
        protected Guid PerformMainGuid => _performMainGuid;

        /// <summary>
        /// 获取或设置此 <see cref="Node"/> 的父 <see cref="Node"/> ；如果传入 <see langword="null"/> ，则会将此 <see cref="Node"/> 设置在根界面容器下。
        /// </summary>
        public Node Parent
        {
            get => _parent;
            set
            {
                if (value != null)
                {
                    if (!transform.IsChildOf(value.transform))
                    {
                        return;
                    }
                    transform.SetParent(value.NodeChildrenContainer.transform);
                }
                else
                {
                    transform.SetParent(NodeRoot.transform);
                }
                RectTransform.anchorMin        = Vector2.zero;
                RectTransform.anchorMax        = Vector2.one;
                RectTransform.anchoredPosition = Vector2.zero;
                RectTransform.sizeDelta        = Vector2.zero;
                _parent                        = value;
            }
        }

        private NodeChildrenContainer NodeChildrenContainer
        {
            get
            {
                if (childrenContainer == null)
                {
                    var instance      = new GameObject(nameof(NodeChildrenContainer));
                    var rectTransform = instance.AddComponent<RectTransform>();
                    rectTransform.SetParent(transform);
                    rectTransform.anchorMin        = Vector2.zero;
                    rectTransform.anchorMax        = Vector2.one;
                    rectTransform.anchoredPosition = Vector2.zero;
                    rectTransform.sizeDelta        = Vector2.zero;
                    childrenContainer              = instance.AddComponent<NodeChildrenContainer>();
                }
                return childrenContainer;
            }
        }

        /// <inheritdoc />
        public override bool IsActive()
        {
            return base.IsActive() && _state == NodeBehaviour.NodeState.Active;
        }

        /// <summary>
        /// 对 <see cref="rectTransforms"/> 中的成员执行布局适配。
        /// </summary>
        /// <param name="rectTransforms">包含要受到布局适配的矩形变换的集合。</param>
        protected virtual void PerformLayoutAdaptation(IEnumerable<RectTransform> rectTransforms)
        {
            foreach (var rectTransform in rectTransforms)
            {
                AlignToSafeArea(rectTransform);
            }
        }

        private void PerformOpen()
        {
            CoroutineRunner.Stop(_performOpenGuid, _performMainGuid, _performCloseGuid);
            CoroutineRunner.Start(PerformOpenEnumerator(), out _performOpenGuid);
        }

        private IEnumerator PerformOpenEnumerator()
        {
            _state                  = NodeBehaviour.NodeState.Inactive;
            _lastActiveUtcDateTime  = DateTime.UtcNow;
            RectTransform.anchorMin = Vector2.zero;
            RectTransform.anchorMax = Vector2.one;
            RectTransform.offsetMin = Vector2.zero;
            RectTransform.offsetMax = Vector2.zero;
            PerformLayoutAdaptation(rectTransformsForAdaptation.Where(e => e != null && e.parent == transform));
            gameObject.SetActive(true);
            yield return Wait.ForEndOfFrame;
            _state                 = NodeBehaviour.NodeState.Enter;
            _lastActiveUtcDateTime = DateTime.UtcNow;
            yield return Enter();
            _state                 = NodeBehaviour.NodeState.Active;
            _lastActiveUtcDateTime = DateTime.UtcNow;
            CoroutineRunner.Start(PerformMain(), out _performMainGuid);
        }

        /// <summary>
        /// 界面的入场。
        /// </summary>
        /// <returns>代表协程的枚举器。</returns>
        protected virtual IEnumerator Enter()
        {
            yield break;
        }

        /// <summary>
        /// 标准入场。
        /// </summary>
        /// <param name="direction">入场。</param>
        /// <param name="fadeIn">是否淡入。</param>
        /// <returns>代表协程的枚举器。</returns>
        protected IEnumerator StandardEnter(Direction direction, bool fadeIn)
        {
            if (direction == Direction.None && !fadeIn)
            {
                yield break;
            }

            if (!Enum.IsDefined(typeof(Direction), direction))
            {
                yield break;
            }

            var localPositionFrom = Vector2.Scale(-direction.ToVector2(), new Vector2(Screen.width, Screen.height));
            var localPositionTo   = Vector2.zero;

            yield return new WaitDuring(
                0.25F,
                t =>
                {
                    transform.SetLocalPosition(Mathf.Lerp(localPositionFrom.x, localPositionTo.x, t), Mathf.Lerp(localPositionFrom.y, localPositionTo.y, t));
                    if (fadeIn)
                    {
                        CanvasGroup.alpha = Mathf.Lerp(0F, 1F, t);
                    }
                }
            );
        }

        /// <summary>
        /// 标准出场。
        /// </summary>
        /// <param name="direction">入场。</param>
        /// <param name="fadeOut">是否淡出。</param>
        /// <returns>代表协程的枚举器。</returns>
        protected IEnumerator StandardExit(Direction direction, bool fadeOut)
        {
            if (direction == Direction.None && !fadeOut)
            {
                yield break;
            }

            if (!Enum.IsDefined(typeof(Direction), direction))
            {
                yield break;
            }

            var localPositionFrom = Vector2.zero;
            var localPositionTo   = Vector2.Scale(direction.ToVector2(), new Vector2(Screen.width, Screen.height));

            yield return new WaitDuring(
                0.25F,
                t =>
                {
                    transform.SetLocalPosition(Mathf.Lerp(localPositionFrom.x, localPositionTo.x, t), Mathf.Lerp(localPositionFrom.y, localPositionTo.y, t));
                    if (fadeOut)
                    {
                        CanvasGroup.alpha = Mathf.Lerp(1F, 0F, t);
                    }
                }
            );
        }

        /// <summary>
        /// 界面要执行的主逻辑。在界面入场结束后开始执行，通过 <see cref="PerformMainGuid"/> 来获取用于停止此协程的全局唯一标识符。
        /// </summary>
        /// <returns>代表协程的枚举器。</returns>
        protected virtual IEnumerator PerformMain()
        {
            yield break;
        }

        /// <summary>
        /// 界面的出场。
        /// </summary>
        /// <returns>代表协程的枚举器。</returns>
        protected virtual IEnumerator Exit()
        {
            yield break;
        }

        private void PerformClose()
        {
            CoroutineRunner.Stop(_performOpenGuid, _performMainGuid, _performCloseGuid);
            CoroutineRunner.Start(PerformCloseEnumerator(), out _performCloseGuid);
        }

        private IEnumerator PerformCloseEnumerator()
        {
            _state                 = NodeBehaviour.NodeState.Exit;
            _lastActiveUtcDateTime = DateTime.UtcNow;
            yield return Exit();
            _state                 = NodeBehaviour.NodeState.Inactive;
            _lastActiveUtcDateTime = DateTime.UtcNow;
            gameObject.SetActive(false);
        }

        private void OnCloseTriggerClick(GameObject go, PointerEventData eventData)
        {
            PerformClose();
        }

        #region 枚举数

        /// <summary>
        /// 以前序返回此界面以及所有子界面。
        /// </summary>
        /// <returns>可前序遍历此 <see cref="Node"/> 以及所有子 <see cref="Node"/> 的枚举数。</returns>
        public IEnumerable<Node> PreOrder()
        {
            yield return this;
            foreach (var child in NodeChildrenContainer.NodeChildren)
            {
                foreach (var node in child.PreOrder())
                {
                    yield return node;
                }
            }
        }

        /// <summary>
        /// 以层序返回此界面以及所有子界面。
        /// </summary>
        /// <returns>可层序遍历此 <see cref="Node"/> 以及所有子 <see cref="Node"/> 的枚举数。</returns>
        public IEnumerable<Node> LevelOrder()
        {
            var queue = new Queue<Node>();
            queue.Enqueue(this);
            while (queue.Count != 0)
            {
                var node = queue.Dequeue();
                foreach (var child in node.NodeChildrenContainer.NodeChildren)
                {
                    queue.Enqueue(child);
                }
                yield return node;
            }
        }

        /// <summary>
        /// 以显示序返回此界面以及所有子界面。
        /// </summary>
        /// <returns>可显示序遍历此 <see cref="Node"/> 以及所有子 <see cref="Node"/> 的枚举数。</returns>
        public IEnumerable<Node> ViewOrder()
        {
            foreach (var child in NodeChildrenContainer.NodeChildren.AsEnumerable().Reverse())
            {
                foreach (var node in child.ViewOrder())
                {
                    yield return node;
                }
            }
            yield return this;
        }

        #endregion

        #region IEnumerable<> member

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable<Node>) this).GetEnumerator();
        }

        IEnumerator<Node> IEnumerable<Node>.GetEnumerator()
        {
            return PreOrder().GetEnumerator();
        }

        #endregion

        #region ICanvasElement member

        /// <inheritdoc />
        public virtual void Rebuild(CanvasUpdate executing) { }

        /// <inheritdoc />
        public virtual void LayoutComplete() { }

        /// <inheritdoc />
        public virtual void GraphicUpdateComplete() { }

        #endregion

        #region Unity event function

        /// <summary>
        /// 为关闭界面的触发器注册点击事件。
        /// </summary>
        protected override void OnEnable()
        {
            base.OnEnable();
            // TODO 省略点击事件注册
            CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
        }

        /// <summary>
        /// 为关闭界面的触发器取消注册点击事件。
        /// </summary>
        protected override void OnDisable()
        {
            CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this);
            // TODO 省略点击事件取消注册
            base.OnDisable();
        }

        /// <summary>
        /// 停止协程，释放资源包。
        /// </summary>
        protected override void OnDestroy()
        {
            StopAllCoroutines();
            AssetBundle.UnloadSafely(true);
            base.OnDestroy();
        }

        #endregion

        #endregion
    }
}
